<template>
  <div class="transfer" v-loading="loading">
    <div class="left">
      <div
        class="selected-num margin12"
        v-if="breadcrumbs && breadcrumbs.length > 0"
      >
        <el-checkbox
          :indeterminate="breadcrumbs[breadcrumbs.length - 1].indeterminate"
          :disabled="checkAllDisabled()"
          v-model="breadcrumbs[breadcrumbs.length - 1].checkAll"
          @change="(value) => changeCheckAll(value)"
          ><span class="checkAll_label">
            {{ checkAllLabel ? checkAllLabel + "：" : ""
            }}{{
              breadcrumbs[breadcrumbs.length - 1].currentCheckedCount || 0
            }}/{{
              breadcrumbs[breadcrumbs.length - 1].membersize +
              " " +
              (unit || "")
            }}
          </span></el-checkbox
        >
      </div>
      <div class="selected-num margin12" v-else>
        <el-checkbox>
          <span class="checkAll_label">
            {{ checkAllLabel ? checkAllLabel + "：" : "" }}{{ 0 }}/{{
              0 + " " + (unit || "")
            }}
          </span></el-checkbox
        >
      </div>
      <div class="margin12" v-if="searchEnable">
        <el-input
          :placeholder="srarchDes"
          :suffix-icon="Search"
          v-model.trim="searchValue"
          @keyup="() => doSearch()"
          @change="() => doSearch()"
          clearable
        ></el-input>
      </div>

      <el-breadcrumb
        separator=">"
        class="margin12-r breadcrumb"
        v-if="breadCrumbEnable && !searchIng"
      >
        <el-breadcrumb-item
          @click="() => breadCrumbClick(item, index)"
          v-for="(item, index) in breadcrumbs"
          :key="index"
          ><template #default>
            <span
              :class="index == breadcrumbs.length - 1 ? 'auto' : 'pointer link'"
              >{{ item[propsMap.label] }}</span
            >
          </template></el-breadcrumb-item
        >
      </el-breadcrumb>

      <el-scrollbar
        :class="
          breadCrumbEnable && !searchIng
            ? searchEnable
              ? ' margin12-r scrollbar-h136'
              : ' margin12-r scrollbar-h92'
            : searchEnable
            ? ' margin12-r scrollbar-h110'
            : ' margin12-r scrollbar-h66'
        "
      >
        <div v-if="treeData && treeData.length > 0">
          <div
            class="option"
            v-for="(item, index) in currentOptions"
            :key="index"
          >
            <el-checkbox
              :key="item[propsMap.value]"
              v-model="item.checked"
              class="checkBodxW"
              :disabled="item.membersize == 0 || item.disabled"
              :indeterminate="item.indeterminate > 0"
              @change="(val) => handleCheckBoxChange(val, item)"
            >
              <template #default v-if="item.typeof == 'Collection'">
                <div class="fll notBreak-l lh3_5">
                  <el-icon v-if="showIcon" class="fll thcolor fs16 h50 mr2">
                    <slot name="childrenIcon"><OfficeBuilding /></slot>
                  </el-icon>
                  <span>{{ item[propsMap.label] }}</span>
                  <span>（{{ item.membersize }}）</span>
                </div>
                <div class="flr w38 h50">
                  <span
                    v-if="item.membersize == 0 || item.checked || item.disabled"
                    @click.prevent="(e) => e.stopPropagation()"
                    :class="item.checked ? 'option-disabled lh3_5' : 'lh3_5'"
                    ><slot name="lowerLevelLabel">下级</slot></span
                  >
                  <a
                    v-else
                    class="link lh3_5"
                    @click.prevent="() => setCurrentOptions(item)"
                    ><slot name="lowerLevelLabel">下级</slot></a
                  >
                </div>
              </template>
              <template
                #default
                v-else-if="
                  item[propsMap.desId] && loopForItemName(item[propsMap.desId])
                "
              >
                <el-icon v-if="showIcon" class="fll thcolor fs16 mt5 mr2">
                  <slot name="memberIcon"><Avatar /></slot>
                </el-icon>
                <span>{{ item[propsMap.label] }}</span
                ><br />
                <span class="option-des">{{
                  loopForItemName(item[propsMap.desId])
                }}</span>
              </template>
              <template #default v-else>
                <el-icon v-if="showIcon" class="fll thcolor fs16 mr2">
                  <slot name="memberIcon"><Avatar /></slot>
                </el-icon>
                <span>{{ item[propsMap.label] }}</span>
              </template>
            </el-checkbox>
          </div>
        </div>
        <div v-else class="no-data-left">{{ noDataContent }}</div>
      </el-scrollbar>
    </div>
    <div class="divider"></div>
    <div class="right">
      <div class="selected-num margin12">
        <span class="lh2"
          >{{ checkedLabel ? checkedLabel + "：" : ""
          }}{{ checkedKeys.length + " " + (unit || "") }}</span
        >
      </div>
      <el-scrollbar class="margin12-r scrollbar-h66">
        <div v-if="selectedObjectList && selectedObjectList.length > 0">
          <div
            class="option-selected"
            v-for="(item, index) in selectedObjectList"
            :key="index"
          >
            <div class="fll notBreak-r" v-if="item.typeof == 'Collection'">
              <el-icon
                v-if="showIcon"
                class="margin10-r fll thcolor h32 lh3_5 fs16"
              >
                <slot name="childrenIcon"><OfficeBuilding /></slot>
              </el-icon>
              <span class="margin10 h32 lh3_5">{{
                item[propsMap.label] + "（" + item.membersize + "）"
              }}</span>
            </div>
            <div
              class="fll option-selected-label"
              v-else-if="
                item[propsMap.desId] && loopForItemName(item[propsMap.desId])
              "
            >
              <el-icon v-if="showIcon" class="fll thcolor fs16 mt5 mr2">
                <slot name="memberIcon"><Avatar /></slot>
              </el-icon>
              <span class="h32 lh1">{{ item[propsMap.label] }}</span
              ><br />
              <span class="option-des lh1">{{
                loopForItemName(item[propsMap.desId])
              }}</span>
            </div>
            <div class="fll notBreak-r" v-else>
              <el-icon
                v-if="showIcon"
                class="margin10-r fll thcolor h32 lh3_5 fs16"
              >
                <slot name="memberIcon"><Avatar /></slot>
              </el-icon>
              <span class="margin10 h32 lh3_5">{{ item[propsMap.label] }}</span>
            </div>
            <div class="flr">
              <span class="margin10 h32 lh3_5"
                ><el-icon
                  v-if="!item.disabled"
                  class="closebold mt18"
                  @click="(e) => closebold(item)"
                  ><slot name="unselectIcon"><CloseBold /></slot></el-icon
              ></span>
            </div>
          </div>
        </div>
        <div v-else class="no-data-right">{{ noDataContent }}</div>
      </el-scrollbar>
    </div>
  </div>
</template>
<script setup>
import { computed } from "@vue/runtime-core";
import { OfficeBuilding, Search, Avatar } from "@element-plus/icons-vue";
import { getCurrentInstance, ref, watch } from "vue";
const proxy = getCurrentInstance();
const checkedMembers = ref([]);
const searchValue = ref("");
const searchIng = ref(false);
const searchResult = ref([]);
const checkedKeys = computed(() =>
  currentTreeData.value
    ? checkedMembers.value.map((member) => member[props.propsMap.value])
    : []
);
const checkedOptionsKeys = ref([]);
const selectedObjectList = ref(undefined);
const currentTreeData = ref(props.treeData[0]);
const breadcrumbs = ref([]);
const currentOptions = computed(() =>
  searchIng.value
    ? searchResult.value
    : loopForOptions(
        breadcrumbs.value.length > 0
          ? breadcrumbs.value[breadcrumbs.value.length - 1]
          : currentTreeData.value
      )
);
const currentTree = ref([]);
const props = defineProps({
  // 当前可选的树结构
  treeData: {
    type: Array,
    default: () => null,
  },
  // 已选择的id数组
  selectedIdList: {
    type: Array,
    default: undefined,
  },
  // 不可编辑的id数组
  disabledIdList: {
    type: Array,
    default: [],
  },
  loading: {
    type: Boolean,
    default: false,
  },
  // 属性定义
  propsMap: {
    type: Object,
    default: () => {
      return {
        value: "id", // ID字段名
        label: "label", // 显示名称
        children: "children", // 子节点
        member: "member", // 节点成员
        desId: "desId", // 节点描述
      };
    },
  },
  // 是否显示图标
  showIcon: {
    type: Boolean,
    default: true,
  },
  // 搜索框显隐
  searchEnable: {
    type: Boolean,
    default: true,
  },
  // 面包屑显隐
  breadCrumbEnable: {
    type: Boolean,
    default: true,
  },
  // 搜索框提示
  srarchDes: {
    type: String,
    default: () => "",
  },
  // 单位
  unit: {
    type: String,
    default: () => "",
  },
  // 全选描述
  checkAllLabel: {
    type: String,
    default: () => null,
  },
  // 已选描述
  checkedLabel: {
    type: String,
    default: () => null,
  },
  // 空数据时的描述
  noDataContent: {
    type: String,
    default: () => "暂无数据",
  },
});

// 获取结果
function getResult() {
  return checkedKeys.value;
}
function getProps() {
  return props;
}

function getTemplate() {
  return [
    {
      label: "",
      value: "",
      children: [
        {
          label: "",
          value: "",
          children: [],
          member: [],
        },
      ],
      member: [
        {
          label: "",
          value: "",
          desId: "",
        },
      ],
    },
  ];
}
defineExpose({
  getResult,
  getProps,
  getTemplate,
});
watch([checkedKeys], () => {
  proxy.emit("update:selectedIdList", checkedKeys.value);
});
watch([props], () => {
  currentTreeData.value = props.treeData[0];
  if (breadcrumbs.value.length > 0 && currentTree.value == props.treeData) {
    const currentBreadcrumb = breadcrumbs.value[breadcrumbs.value.length - 1];
    currentBreadcrumb.membersize = countAll(currentBreadcrumb);
    if (
      props.selectedIdList &&
      checkedOptionsKeys.value != props.selectedIdList &&
      !selectedObjectList.value
    ) {
      checkedOptionsKeys.value = props.selectedIdList;
      loopForMemberSetChildFalse([currentBreadcrumb]).forEach((member) => {
        if (checkedOptionsKeys.value.includes(member[props.propsMap.value])) {
          handleCheckBoxChange(true, member);
        } else {
          handleCheckBoxChange(false, member);
        }
      });
    }
  } else {
    if (currentTreeData.value) {
      init();
      currentTreeData.value.membersize = countAll(currentTreeData.value);
      if (props.selectedIdList) {
        checkedOptionsKeys.value = props.selectedIdList;
        loopForMemberSetChildFalse([currentTreeData.value]).forEach(
          (member) => {
            if (props.selectedIdList.includes(member[props.propsMap.value])) {
              handleCheckBoxChange(true, member);
            } else {
              handleCheckBoxChange(false, member);
            }
          }
        );
      }
    } else {
      breadcrumbs.value = [];
      selectedObjectList.value = [];
    }
  }
});

async function init() {
  currentTree.value = props.treeData;
  breadcrumbs.value = [];
  checkedMembers.value = [];
  selectedObjectList.value = [];
}

function setCurrentOptions(item) {
  // 计算总数
  item.membersize = countAll(item);
  // 计算已选的数
  countCurrentCheckedOptions();
}

/**
 * 勾选checkbox
 * @param {*} value
 * @param {*} item
 */
function handleCheckBoxChange(value, item) {
  item.indeterminate = false;
  item.checked = value;
  updateSelected(value, item);
}

function cancelChecked(item) {
  const { children, member } = item;
  let total = [];
  if (children)
    children.forEach((child) => {
      let one = [];
      if (!child.disabled) {
        child.checked = false;
        one.push(child[props.propsMap.value]);
        if (child.children) {
          one = one.concat(cancelChecked(child));
        }
        total = total.concat(one);
      }
    });
  if (member)
    member.forEach((member) => {
      if (!member.disabled) {
        member.checked = false;
        total.push(member[props.propsMap.value]);
      }
    });
  return total;
}

/**
 * 计算当前组织中被选中的成员数
 */
function countCurrentCheckedOptions(loop) {
  const currentObject = loop || breadcrumbs.value[breadcrumbs.value.length - 1];
  currentObject.currentCheckedCount = 0;
  const { member, children } = currentObject;
  if (children) {
    children.forEach((child) => {
      countCurrentCheckedOptions(child);
      if (child.checked) {
        currentObject.currentCheckedCount += child.membersize;
      }
      // 发现是中间状态
      if (child.indeterminate) {
        currentObject.currentCheckedCount += child.currentCheckedCount;
      }
    });
  }
  if (member) {
    member.forEach((member) => {
      if (member.checked) {
        currentObject.currentCheckedCount += 1;
      }
    });
  }
  currentObject.checkAll =
    currentObject.currentCheckedCount >= currentObject.membersize &&
    currentObject.membersize != 0;
  currentObject.indeterminate =
    currentObject.currentCheckedCount < currentObject.membersize &&
    currentObject.currentCheckedCount > 0;
  currentObject.checked = currentObject.checkAll;
}

function breadCrumbClick(item, index) {
  // 移除后面的面包屑
  breadcrumbs.value = breadcrumbs.value.slice(0, index);
  breadcrumbs.value.push(item);
  item.membersize = countAll(item);
  countCurrentCheckedOptions();
}

function countAll(data) {
  const allMember = loopForOptions(data);
  let count = 0;
  allMember.forEach((item) => {
    // disabled 不算入总数
    if (
      !props.disabledIdList.includes(item[props.propsMap.value]) ||
      props.selectedIdList.includes(item[props.propsMap.value])
    ) {
      if (item.typeof == "Collection") {
        count += item.membersize;
      } else {
        count += 1;
      }
    }
  });
  return count;
}

function loopForOptions(data) {
  let result = [];
  if (data && JSON.stringify(data) != "{}") {
    // 可选
    data.disabled = props.disabledIdList.includes(data[props.propsMap.value]);
    const { children, member } = data;
    if (children && children.length) {
      loopForObjectNumber(children);
      result = children;
    }
    if (member && member.length) {
      member.forEach((m) => {
        m.disabled = props.disabledIdList.includes(m[props.propsMap.value]);
      });
      result = result.concat(member);
    }
    // 添加面包屑
    if (
      breadcrumbs.value.filter(
        (breadcrumb) =>
          data[props.propsMap.label] == breadcrumb[props.propsMap.label]
      ).length == 0
    ) {
      breadcrumbs.value.push(data);
    }
  }
  return result;
  // 总数
  // 已选
}
function loopForObjectNumber(children) {
  let total = 0;
  children.forEach((child) => {
    let one = 0;
    // 将集合节点的类型赋值为Collection
    child.typeof = "Collection";
    child.disabled = props.disabledIdList.includes(child[props.propsMap.value]);
    const { member, children } = child;
    if (children && children.length > 0) {
      one += loopForObjectNumber(children);
    }
    if (member && member.length > 0) {
      member.forEach((m) => {
        m.disabled = props.disabledIdList.includes(m[props.propsMap.value]);
      });
      one += member.length;
    }
    total += one;
    child.membersize = one;
  });
  return total;
}

function loopForMember(data, value) {
  let total = [];
  data.forEach((child) => {
    if (value || !child.disabled) {
      child.checked = value;
    }
    let one = [];
    const { member, children } = child;
    if (children && children.length > 0) {
      one = one.concat(loopForMember(children, value));
    }
    if (member && member.length > 0) {
      member.forEach((mem) => {
        if (!mem.disabled || value) {
          mem.checked = value;
        }
        one.push(mem);
      });
    }
    total = total.concat(one);
  });
  return total;
}
function resetSelectedMembers(value, members) {
  members.forEach((mem) => {
    const hasMember = checkedMembers.value.filter(
      (member) => member[props.propsMap.value] == mem[props.propsMap.value]
    );
    if (value && hasMember.length == 0) {
      checkedMembers.value.push(mem);
    } else {
      if (!value && !mem.disabled)
        checkedMembers.value = checkedMembers.value.filter(
          (member) => member[props.propsMap.value] != mem[props.propsMap.value]
        );
    }
  });
}
function findName(data, id) {
  let label = null;
  data.forEach((tree) => {
    if (id == tree[props.propsMap.value]) {
      label = tree[props.propsMap.label];
    } else if (
      tree[props.propsMap.children] &&
      tree[props.propsMap.children].length > 0 &&
      !label
    ) {
      label = findName(tree[props.propsMap.children], id);
    }
  });
  return label;
}

function loopForItemName(id) {
  return findName(props.treeData, id);
}
function changeCheckAll(value) {
  const currentObject = breadcrumbs.value[breadcrumbs.value.length - 1];
  currentObject.indeterminate = false;
  currentObject.checked = value;
  const { children, member } = currentObject;
  if (children) {
    children.forEach((item) => {
      // 后面if判断中还能加disabled
      if (item.membersize != 0 && !item.disabled) {
        item.indeterminate = false;
        item.checked = value;
        updateSelected(value, item);
      }
    });
  }
  if (member) {
    // 后面if判断中还能加disabled
    member.forEach((item) => {
      if (!item.disabled) {
        item.checked = value;
        updateSelected(value, item);
      }
    });
  }
}

function updateSelected(value, item) {
  pushOrCancelRight(value, item);
  // 2. 根据选中情况，筛选出可选的成员
  let editMembers = [];
  if (item.typeof == "Collection") {
    editMembers = loopForMember([item], value);
  } else {
    editMembers = [item];
  }
  // 重置被选中的成员总数
  resetSelectedMembers(value, editMembers);
  // 设置当前节点被选中的数量，同时重置全选状态
  countCurrentCheckedOptions();
}
function pushOrCancelRight(value, item) {
  if (!selectedObjectList.value) {
    selectedObjectList.value = [];
  }
  // 1. 将选中的对象进行添加和删除
  if (value) {
    selectedObjectList.value = selectedObjectList.value.filter(
      (obj) => item[props.propsMap.value] != obj[props.propsMap.value]
    );
    selectedObjectList.value.push(item);
  } else {
    let cancelKeys = cancelChecked(item);
    selectedObjectList.value = selectedObjectList.value.filter(
      (obj) =>
        obj[props.propsMap.value] != item[props.propsMap.value] &&
        !cancelKeys.includes(obj[props.propsMap.value])
    );
  }
}

function closebold(item) {
  item.checkAll = false;
  item.checked = false;
  item.indeterminate = false;
  updateSelected(false, item);
}
let timer = null;
function doSearch() {
  if (timer) {
    clearTimeout(timer);
  }
  timer = setTimeout(() => {
    if (searchValue.value) {
      // 先找符合的集合
      const childrenMembers = loopForMemberFormChildren(
        props.treeData,
        searchValue.value.toLowerCase()
      );
      // 再找符合的成员
      const members = loopForMemberByLabel(
        props.treeData,
        searchValue.value.toLowerCase()
      );
      const membersValue = members.map(
        (member) => member[props.propsMap.value]
      );
      const complementarySet = childrenMembers.filter(
        (member) => !membersValue.includes(member[props.propsMap.value])
      );
      searchResult.value = members.concat(complementarySet);
      const searchResultObj = {
        currentCheckedCount: searchResult.value.filter(
          (reuslt) => reuslt.checked
        ).length,
        membersize: searchResult.value.length,
        member: searchResult.value,
        children: [],
      };
      breadcrumbs.value = [];
      breadcrumbs.value.push(searchResultObj);
      countCurrentCheckedOptions();
      searchIng.value = true;
    } else {
      breadcrumbs.value = [];
      breadcrumbs.value.push(currentTreeData.value);
      countCurrentCheckedOptions();
      searchIng.value = false;
    }
  }, 300);
}
function loopForMemberFormChildren(data, label) {
  let total = [];
  data.forEach((child) => {
    let one = [];
    const { member, children } = child;
    if (children && children.length > 0) {
      if (child[props.propsMap.label].toLowerCase().includes(label)) {
        one = one.concat(loopForMemberNotSetVal(children));
      }

      one = one.concat(loopForMemberFormChildren(children, label));
    }
    if (member && member.length > 0) {
      member.forEach((mem) => {
        if (child[props.propsMap.label].includes(label)) {
          one.push(mem);
        }
      });
    }
    total = total.concat(one);
  });
  return total;
}
function loopForMemberByLabel(data, label) {
  let total = [];
  data.forEach((child) => {
    let one = [];
    const { member, children } = child;
    if (children && children.length > 0) {
      one = one.concat(loopForMemberByLabel(children, label));
    }
    if (member && member.length > 0) {
      member.forEach((mem) => {
        if (mem[props.propsMap.label].toLowerCase().includes(label)) {
          one.push(mem);
        }
      });
    }
    total = total.concat(one);
  });
  return total;
}
function loopForMemberNotSetVal(data) {
  let total = [];
  data.forEach((child) => {
    let one = [];
    const { member, children } = child;
    if (children && children.length > 0) {
      one = one.concat(loopForMemberNotSetVal(children));
    }
    if (member && member.length > 0) {
      member.forEach((mem) => {
        one.push(mem);
      });
    }
    total = total.concat(one);
  });
  return total;
}
function loopForMemberSetChildFalse(data) {
  let total = [];
  data.forEach((child) => {
    handleCheckBoxChange(false, child);
    let one = [];
    const { member, children } = child;
    if (children && children.length > 0) {
      one = one.concat(loopForMemberSetChildFalse(children));
    }
    if (member && member.length > 0) {
      member.forEach((mem) => {
        one.push(mem);
      });
    }
    total = total.concat(one);
  });
  return total;
}
function checkAllDisabled() {
  let reuslt = false;
  const currentBreadcrumb = breadcrumbs.value[breadcrumbs.value.length - 1];
  if (currentBreadcrumb) {
    const { member, children } = currentBreadcrumb;
    let childrenDisabled = 0;
    let memberDisabled = 0;
    if (children) {
      const childrenDisabledList = children.filter((c) => c.disabled);
      if (childrenDisabledList) {
        childrenDisabled = childrenDisabledList.length;
      }
    }
    if (member) {
      const memberDisabledList = member.filter((m) => m.disabled);
      if (memberDisabledList) {
        memberDisabled = memberDisabledList.length;
      }
    }
    if (
      childrenDisabled == children.length &&
      memberDisabled == member.length
    ) {
      reuslt = true;
    }
  }
  return reuslt;
}
</script>
<style lang="scss" scoped>
@import "./index.scss";
</style>